<?php

namespace App\Console\Commands\Init;

use App\Common\Libs\ArrayHelper;
use App\Common\Libs\DocParser;
use App\Common\Libs\Json;
use App\Common\Libs\YAPI;
use App\Console\BaseCommand;
use Illuminate\Support\Facades\DB;

class DocCommand extends BaseCommand
{
    protected $signature = 'init:doc {action} {--class=} {--method=}';

    protected $description = 'Init doc';

    public function __construct()
    {
        parent::__construct();
    }

    public function all()
    {

        $path = app_path() . '/Http/Controllers/Api';

        DB::beginTransaction();
        try {
            foreach (scandir($path) as $filename) {
                if ($filename === '.' || $filename === '..' || strpos($filename, 'php') === false) {
                    continue;
                }

                $className = mb_substr($filename, 0, -4);
                $this->_saveDocByClassName($className);
            }
            DB::commit();

            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            $this->error("请注意此次操作依据回滚 {$e->getMessage()} - {$e->getLine()}");
            return false;
        }
    }

    public function file()
    {
        $params = $this->getParams();
        if (empty($params['class'])) {
            $this->error('请检查是否正确传入class');
            return false;
        }

        if (!empty($params['method'])) {
            $this->error('您传入了method参数，请使用fun方法');
            return false;
        }

        $className = ucwords($params['class']) . 'Controller';
        DB::beginTransaction();
        try {
            $this->_saveDocByClassName($className);

            DB::commit();

            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            $this->error("请注意此次操作依据回滚 {$e->getMessage()} - {$e->getLine()}");
            return false;
        }
    }

    public function fun()
    {
        $params = $this->getParams();
        if (empty($params['class']) || empty($params['method'])) {
            $this->error('请检查是否正确传入class和method');
            return false;
        }

        $className = ucwords($params['class']) . 'Controller';
        $method = $params['method'];

        DB::beginTransaction();
        try {
            $this->_saveDocByMethod($method, $className);

            DB::commit();

            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            $this->error("请注意此次操作依据回滚 {$e->getMessage()} - {$e->getLine()}");
            return false;
        }
    }

    /**
     * 通过类名上传到yapi
     * @param $className
     * @return bool
     */
    private function _saveDocByClassName($class)
    {
        $judgeRes = $this->_judgeFileExistsByClass($class);
        if (!$judgeRes) {
            return false;
        }

        $className = strpos($class, 'App\Http\\') !== false ? $class : 'App\Http\Controllers\Api\\' . $class;

        $reflection = new \ReflectionClass($className);
        $methods = $reflection->getMethods();
        foreach ($methods as $method) {
            try {
                if ($method->class == $className) {
                    $this->_saveDocByMethod($method->name, $class);
                }
            } catch (\Exception $e) {
                $this->error("{$className} - {$method->name} 注释不符合规范");
                continue;
            }
        }


        return true;
    }

    /**
     * 通过方法名上传到yapi
     * @param $methodName
     * @param $class
     * @return bool
     */
    private function _saveDocByMethod($methodName, $class)
    {
        $judgeRes = $this->_judgeFileExistsByClass($class);
        if (!$judgeRes) {
            return false;
        }

        $className = strpos($class, 'App\Http\\') !== false ? $class : 'App\Http\Controllers\Api\\' . $class;

        $reflection = new \ReflectionClass($className);
        $methods = $reflection->getMethods();
        $methods = ArrayHelper::index($methods, 'name');
        if (empty($methods[$methodName])) {
            $this->error("{$className} - {$methodName} 方法不存在");

            return false;
        }
        $method = $methods[$methodName];
        $docParser = new DocParser();
        $doc = $method->getDocComment();
        $params = $docParser->parse($doc);
        $yapiData = $this->_getYapiData($params);
        $response = $this->_saveDoc($yapiData);
        $responseContents = $this->_getContents($response);
        if ($responseContents['errcode'] == 0) {
            $this->info($className . " - {$method->name} 上传Yapi成功");
        } else {
            $this->comment($className . " - {$method->name} 上传Yapi失败 " . Json::encode($responseContents));
        }

        return true;
    }

    /**
     * 获取上传yapi数据
     * @param $params
     * @return array
     */
    private function _getYapiData($params)
    {
        $projectId = $this->_getProjectId();
        $categoryMenus = $this->_getCategoryMenu();
        if (empty($categoryMenus[$params['category']])) {
            $result = $this->_addCategory([
                'name' => $params['category'],
                'project_id' => $projectId,
            ]);
            $categoryId = $result['_id'];
        } else {
            $categoryId = $categoryMenus[$params['category']];
        }

        return [
            'path' => $params['path'],
            'title' => $params['title'],
            'project_id' => $projectId,
            'catid' => $categoryId,
            'method' => $params['method'],
            'tag' => array_key_exists('tag', $params) ? $params['tag'] : [],
            'status' => array_key_exists('status', $params) ? YAPI::$status[$params['status']] : YAPI::STATUS_FINISHED,
            'req_body_form' => array_key_exists('params', $params) ? Json::decode($params['params']) : [],
            'res_body' => array_key_exists('resBody', $params) ? $params['resBody'] : '',
            'res_body_type' => array_key_exists('resBodyType', $params) ? $params['resBodyType'] : YAPI::RES_BODY_TYPE_RAW,
            'desc' => array_key_exists('description', $params) ? $params['description'] : '',
            'markdown' => array_key_exists('description', $params) ? $params['description'] : '',
        ];
    }

    private function _judgeFileExistsByClass($class)
    {
        $filename = app_path() . '/Http/Controllers/Api/' . $class . '.php';
        if (!file_exists($filename)) {
            $this->error("{$filename} 文件不存在");

            return false;
        }

        return true;
    }

    /**
     * 获取YAPI配置
     * @return array
     */
    private function _getConfig()
    {
        return [
            'username' => env('YAPI_USERNAME'),
            'password' => env('YAPI_PASSWORD'),
            'group_name' => env('YAPI_GROUP_NAME'),
            'project_name' => env('YAPI_PROJECT_NAME'),
        ];
    }

    /**
     * 获取YAPI对象
     * @return YAPI
     */
    private function _addCategory($params)
    {
        $yapi = $this->_getYapi();

        return $yapi->addCategory($params);
    }

    /**
     * 获取YAPI对象
     * @return YAPI
     */
    private function _getYapi()
    {
        $config = $this->_getConfig();

        return YAPI::getInstance($config['username'], $config['password']);
    }

    /**
     * 获取项目ID
     * @return mixed
     */
    private function _getProjectId()
    {
        $config = $this->_getConfig();

        $yapi = $this->_getYapi();

        return $yapi->getOneProject($config['group_name'], $config['project_name'])[$config['project_name']];
    }

    /**
     * 获取导航
     * @return array|\Illuminate\Http\JsonResponse
     */
    private function _getCategoryMenu()
    {
        $config = $this->_getConfig();

        $yapi = $this->_getYapi();

        return $yapi->getCategoryMenu($config['group_name'], $config['project_name']);
    }

    /**
     * 上传Yapi
     * @param $yapiData
     * @return \Psr\Http\Message\ResponseInterface
     */
    private function _saveDoc($yapiData)
    {
        $yapi = $this->_getYapi();

        return $yapi->saveDoc($yapiData);
    }

    /**
     * 获取response内容
     * @param $response
     * @return \Illuminate\Http\JsonResponse|mixed|null
     */
    private function _getContents($response)
    {
        $yapi = $this->_getYapi();

        return $yapi->getContents($response);
    }
}
